﻿using System;
using System.Collections.Generic;
using System.Linq;
using AMO = Microsoft.AnalysisServices;

namespace AmoExtensions
{
    /*
     * SSAS Partition Manager
     * https://ssaspartitionmanager.codeplex.com/
     * Written by Dr. John Tunnicliffe of Decision Analytics, April 2015
     * eMail: john@decision-analytics.co.uk 
     * http://www.decision-analytics.co.uk/
     * https://www.linkedin.com/in/drjohntunnicliffe
     */

    /// <summary>
    /// Extends various Microsoft.AnalysisServices classes with new methods
    /// </summary>
    public static class AmoClassExtensions
    {
        /// <summary>
        /// Extends the Microsoft.AnalysisServices.Server class with a new method which obtains a
        /// reference to particular database on the server with a specific name or ID
        /// </summary>
        /// <remarks>Generates an error if the database can't be found.</remarks>
        /// <param name="databaseNameOrId">The name or ID of the database to find.</param>
        /// <returns>A <c>Database</c> instance.</returns>
        public static AMO.Database Database(this AMO.Server server, string databaseNameOrId)
        {
            if (server == null) throw new ArgumentNullException("server");
            if (databaseNameOrId.IsNullOrEmptyOrWhitespace()) throw new ArgumentNullException("databaseNameOrId");

            AMO.Database database;
            try
            {
                database = server.Databases[databaseNameOrId];
            }
            catch
            {
                database = server.Databases.FindByName(databaseNameOrId);
            }
            if (database == null)
            {
                string msg = string.Format("Database '{0}' not found on server '{1}'", databaseNameOrId, server.Name);
                throw new ApplicationException(msg);
            }
            return database;
        }

        /// <summary>
        /// Extends the Microsoft.AnalysisServices.Database class with a new method which obtains a
        /// reference to particular cube in the database with a specific name or ID
        /// </summary>
        /// <remarks>Generates an error if the cube can't be found.</remarks>
        /// <param name="databaseNameOrId">The name or ID of the cube to find.</param>
        /// <returns>A <c>Cube</c> instance.</returns>
        public static AMO.Cube Cube(this AMO.Database database, string cubeNameOrId)
        {
            if (database == null) throw new ArgumentNullException("database");
            if (cubeNameOrId.IsNullOrEmptyOrWhitespace()) throw new ArgumentNullException("cubeNameOrId");

            AMO.Cube cube;
            try
            {
                cube = database.Cubes[cubeNameOrId];
            }
            catch
            {
                cube = database.Cubes.FindByName(cubeNameOrId);
            }
            if (cube == null)
            {
                string msg = string.Format("Cube '{0}' not found in database '{1}'", cubeNameOrId, database.Name);
                throw new ApplicationException(msg);
            }
            return cube;
        }

        /// <summary>
        /// Extends the Microsoft.AnalysisServices.Cube class with a new method which obtains a
        /// reference to particular measure group in the cube with a specific name or ID
        /// </summary>
        /// <remarks>Generates an error if the MeasureGroup can't be found.</remarks>
        /// <param name="databaseNameOrId">The name or ID of the cube to find.</param>
        /// <returns>A <c>Cube</c> instance.</returns>
        public static AMO.MeasureGroup MeasureGroup(this AMO.Cube cube, string measureGroupNameOrId)
        {
            if (cube == null) throw new ArgumentNullException("cube");
            if (measureGroupNameOrId.IsNullOrEmptyOrWhitespace()) throw new ArgumentNullException("measureGroupNameOrId");

            AMO.MeasureGroup measureGroup;
            try
            {
                measureGroup = cube.MeasureGroups[measureGroupNameOrId];
            }
            catch
            {
                measureGroup = cube.MeasureGroups.FindByName(measureGroupNameOrId);
            }

            if (measureGroup == null)
            {
                string msg = string.Format("Measure group '{0}' not found in cube '{1}'", measureGroupNameOrId, cube.Name);
                throw new ApplicationException(msg);
            }
            return measureGroup;
        }

        /// <summary>
        /// Extends the Microsoft.AnalysisServices.MeasureGroup class with a new method which obtains a
        /// reference to particular partition based on its name or ID. 
        /// </summary>
        /// <remarks>Returns null if the partition is not found</remarks>
        /// <param name="measureGroup">The measure group upon which the new method is added</param>
        /// <param name="partitionIdOrName">The partition's name or ID</param>
        /// <returns>The matching partition</returns>
        public static AMO.Partition FindPartition(this AMO.MeasureGroup measureGroup, string partitionIdOrName)
        {
            if (measureGroup == null) throw new ArgumentNullException("measureGroup");
            if (partitionIdOrName.IsNullOrEmptyOrWhitespace()) throw new ArgumentNullException("partitionIdOrName");

            AMO.Partition returnPartition = null;
            try
            {
                returnPartition = measureGroup.Partitions[partitionIdOrName];
            }
            catch
            {
                returnPartition = measureGroup.Partitions.FindByName(partitionIdOrName);
            }

            if (returnPartition == null)
            {
                string msg = string.Format("Partition '{0}' not found in cube '{1}'", partitionIdOrName, measureGroup.Name);
                throw new ApplicationException(msg);
            }
            return (returnPartition);
        }

        /// <summary>
        /// Extends the Microsoft.AnalysisServices.MeasureGroup class with a new method which obtains a
        /// reference to a partition containing the word "Template" in its name.
        /// </summary>
        /// <remarks>Throws and error if no template partition found</remarks>
        /// <param name="measureGroup">The measure group upon which the new method is added</param>
        /// <returns>The template partition as an AMO.Partition</returns>
        public static AMO.Partition GetTemplatePartition(this AMO.MeasureGroup measureGroup)
        {
            if (measureGroup == null) throw new ArgumentNullException("measureGroup");

            AMO.Partition templatePartition = null;
            foreach (AMO.Partition partition in measureGroup.Partitions)
            {
                if (partition.Name.ToLower().Contains("template"))
                {
                    templatePartition = partition;
                    break;
                }
            }

            if (templatePartition == null)
            {
                string msg = string.Format("Template partition not found in measure group '{0}'", measureGroup.Name);
                throw new ApplicationException(msg);
            }

            return (templatePartition);
        }

        /// <summary>
        /// Extends the string class with a method that checks if the string is null, empty or contains whitespace
        /// </summary>
        /// <param name="s">String to be tested</param>
        /// <returns>True if null, empty or contains whitespace</returns>
        public static bool IsNullOrEmptyOrWhitespace(this string s)
        {
            return string.IsNullOrEmpty(s) || string.IsNullOrWhiteSpace(s);
            //This change retains the functionality and doesn't requiere the assembly to be compiled with .Net 4.0
            //return string.IsNullOrEmpty(s) || (s.Trim().Length == 0);
        }
    }
}
